package nginx

import (
	"bufio"
	"io"
	"strconv"
	"strings"
	"time"

	"gitee.com/bjf-fhe/apicat/entry"
	"gitee.com/bjf-fhe/apicat/source"
	"gitee.com/bjf-fhe/apicat/utils"
	"github.com/satyrius/gonx"
	"github.com/sirupsen/logrus"
)

const DefaultLocalTimeFormat = "02/Jan/2006:15:04:05 -0700"

func NewFromNginx(ne *gonx.Entry, tf string) *entry.LogEntry {
	entry := new(entry.LogEntry)
	request, err := ne.Field("request")
	if err == nil {
		parts := strings.SplitN(request, " ", 3)
		if len(parts) > 1 {
			entry.Method = parts[0]
			entry.Url = parts[1]
		}
		if len(parts) > 2 {
			entry.Protocol = parts[2]
		}
	}

	tl, err := ne.Field("time_local")
	if err == nil && tl != "" {
		entry.Created, err = time.Parse(tf, tl)
		if err != nil {
			logrus.Error("解析时间错误")
		}
	}

	tl, err = ne.Field("time_iso8601")
	if err == nil && tl != "" {
		entry.Created, err = time.Parse(time.RFC3339, tl)
		if err != nil {
			logrus.Error("解析时间错误")
		}
	}

	code, err := ne.Field("status_code")
	if err == nil {
		entry.StatusCode, _ = strconv.Atoi(code)
	}
	entry.Client, _ = ne.Field("remote_addr")
	return entry
}

type Reader struct {
	file       io.Reader
	parser     gonx.StringParser
	timeFormat string
	tailMode   bool
	notifier   chan bool
	entries    chan *entry.LogEntryResponse
}

func (r *Reader) Records() chan *entry.LogEntryResponse {
	return r.entries
}

func (r *Reader) wait(tailMode bool) {
	fileScanner := bufio.NewScanner(r.file)
	for {
		var hasLine = fileScanner.Scan()
		if !hasLine {
			if tailMode {
				if r.notifier != nil {
					<-r.notifier
				} else {
					logrus.Debug("used in waiting mode,waiting for 3 seconds")
					time.Sleep(3 * time.Second)
				}
				continue
			} else {
				r.entries <- &entry.LogEntryResponse{
					Error: fileScanner.Err(),
				}
			}
		}
		ne, err := r.parser.ParseString(fileScanner.Text())
		var item *entry.LogEntry
		if err == nil {
			item = NewFromNginx(ne, r.timeFormat)
		}
		r.entries <- &entry.LogEntryResponse{
			Entry: item,
			Error: err,
		}
		if !tailMode && err != nil {
			logrus.Error(err)
			break
		}
	}
}

func NewReader(logFile io.Reader, nginxConf io.Reader, formatName string, timeFormat string, tailMode bool) (source.Reader, error) {
	parser, err := gonx.NewNginxParser(nginxConf, formatName)

	if err == nil {
		var reader = &Reader{
			file:       logFile,
			parser:     parser,
			timeFormat: timeFormat,
			entries:    make(chan *entry.LogEntryResponse),
		}
		if n, ok := logFile.(utils.BufCanBeNotified); ok {
			reader.notifier = make(chan bool, 1)
			n.Subscribe(reader.notifier)
		}
		go reader.wait(tailMode)
		return reader, nil
	}
	return nil, err
}
